﻿using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using yzrilyzr.util;

namespace FAQ_UI
{
	public class MyDrawableView : FrameworkElement, Drawable.InvalidateListener
	{
		internal static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(Uri), typeof(MyDrawableView), new UIPropertyMetadata(null, DrawableChange));
		internal static readonly DependencyProperty DrawableProperty = DependencyProperty.Register("Drawable", typeof(Drawable), typeof(MyDrawableView), new UIPropertyMetadata(null, DrawableChange));
		internal static readonly DependencyProperty StretchProperty = DependencyProperty.Register("Stretch", typeof(Stretch), typeof(MyDrawableView), new UIPropertyMetadata(Stretch.Uniform, DrawableChange));

		public MyDrawableView()
		{
			RenderOptions.SetBitmapScalingMode(this, System.Windows.Media.BitmapScalingMode.HighQuality);
		}

		private static void DrawableChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			MyDrawableView dd = (MyDrawableView)d;
			if (e.Property == DrawableProperty)
			{
				Drawable value = e.NewValue as Drawable;
				dd.setChild(null);
				if (dd.Drawable != null)
				{
					dd.Drawable.setInvalidateListener(null);
					dd.Drawable.View = null;
				}
				if (value != null)
				{
					dd.setChild(value.getChild());
					value.setInvalidateListener(dd);
					value.View = dd;
				}

			}
			dd.InvalidateMeasure();
			dd.InvalidateVisual();
		}

		public Drawable Drawable
		{
			get => (Drawable)GetValue(DrawableProperty);
			set => SetValue(DrawableProperty, value);
		}

		private void setChild(FrameworkElement add)
		{
			if (child != null)
			{
				RemoveVisualChild(child);
				RemoveLogicalChild(child);
			}
			//if(add.pa)
			AddVisualChild(add);
			AddLogicalChild(add);
			this.child = add;

		}

		protected override int VisualChildrenCount
		{
			get { return child == null ? 0 : 1; }
		}

		protected override Visual GetVisualChild(int index)
		{
			return child;
		}

		public Stretch Stretch
		{
			get => (Stretch)GetValue(StretchProperty);
			set => SetValue(StretchProperty, value);
		}

		public Uri Source
		{
			get => (Uri)GetValue(SourceProperty);
			set => SetValue(SourceProperty, value);
		}

		private Rect getScaledRect(double parentW, double parentH)
		{
			double scale = 1;
			double cW = Drawable.getIntrinsicWidth();
			double cH = Drawable.getIntrinsicHeight();
			if (cW == 0) cW = parentW;
			if (cH == 0) cH = parentH;
			if (cW == 0 || cH == 0) throw new Exception("Parent size == 0");
			double sc1 = parentW / cW;
			double sc2 = parentH / cH;
			bool lgtW = cW * sc2 > parentW;
			bool lgtH = cH * sc1 > parentH;
			if (Stretch == Stretch.Uniform)
			{
				if (lgtH) scale = sc2;
				else if (lgtW) scale = sc1;
				else scale = (sc1 + sc2) / 2.0;
			}
			else if (Stretch == Stretch.UniformToFill)
			{
				if (lgtH) scale = sc1;
				else if (lgtW) scale = sc2;
				else scale = (sc1 + sc2) / 2.0;
			}
			double rW = cW * scale;
			double rH = cH * scale;
			double bx = parentW / 2 - rW / 2;
			double by = parentH / 2 - rH / 2;
			return new Rect(bx, by, rW, rH);
		}

		protected override Size MeasureOverride(Size availableSize)
		{
			if (Drawable == null)
			{
				availableSize.Width = 0;
				availableSize.Height = 0;
				return availableSize;
			}
			double iW = Drawable.getIntrinsicWidth();
			double iH = Drawable.getIntrinsicHeight();
			if (iW == 0 || iH == 0)
			{
				availableSize.Width = 0;
				availableSize.Height = 0;
				return availableSize;
			}
			bool wIsI = Double.IsInfinity(availableSize.Width);
			bool hIsI = Double.IsInfinity(availableSize.Height);
			if (wIsI && hIsI)
			{
				availableSize.Width = iW;
				availableSize.Height = iH;
			}
			else if (wIsI)
			{
				double scale;
				if (iH == 0) scale = 1;
				else scale = availableSize.Height / iH;
				availableSize.Width = iW * scale;
			}
			else if (hIsI)
			{
				double scale;
				if (iW == 0) scale = 1;
				else scale = availableSize.Width / iW;
				availableSize.Height = iH * scale;
			}
			if (iW < availableSize.Width) availableSize.Width = iW;
			if (iH < availableSize.Height) availableSize.Height = iH;
			Console.WriteLine(availableSize);
			return availableSize;
		}

		private Typeface typeface = new Typeface("Arial");
		private FPSUtil FPSUtil = new FPSUtil();
		private FrameworkElement child;

		protected override void OnRender(DrawingContext dc)
		{
			if (RenderSize.Width == 0 || RenderSize.Height == 0) return;
			if (Drawable == null) return;
			if (Visibility != Visibility.Visible) return;
			if (Stretch == Stretch.Fill) Drawable.setBounds(0, 0, RenderSize.Width, RenderSize.Height);
			else Drawable.setBounds(getScaledRect(RenderSize.Width, RenderSize.Height));
			Drawable.draw(dc);
			/*double fpsTime = FPSUtil.computeFrameTime();
			double fpsLow = FPSUtil.computeLow(100);
			if (fpsTime != 0) dc.DrawText(new FormattedText(
				string.Format("FPS:{0} Low 1%:{1}",
				Convert.ToDouble(1000 / fpsTime).ToString("0.0"),
				Convert.ToDouble(1000 / fpsLow).ToString("0.0")),
				CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, 20, Theme.AccentBrush), new Point(0, 0));*/
			if (Drawable.invalidate)
			{
				Drawable.invalidate = false;
				Invalidate();
			}
		}

		private async void Invalidate()
		{
			await Task.Delay(10);
			InvalidateVisual();
		}

		public void drawableInvalidate()
		{
			Invalidate();
		}
	}
}